import Glibc

class Timer {
    private let CLOCK_REALTIME = 0
    private var start_timespec = timespec()
    private var end_timespec = timespec()
    private var time_spec = timespec()
    func start() {
        clock_gettime(Int32(CLOCK_REALTIME),&start_timespec)
    }
    
    func stop() -> Double {
        clock_gettime(Int32(CLOCK_REALTIME),&end_timespec)
        let start_time = Double(start_timespec.tv_sec * 1_000_000 + start_timespec.tv_nsec / 1_000)
        let end_time = Double(end_timespec.tv_sec * 1_000_000 + end_timespec.tv_nsec / 1_000)
        let time = end_time - start_time
        return time / 1_000
    }
    func getTime() -> Double {
        clock_gettime(Int32(CLOCK_REALTIME),&time_spec)
        return Double(time_spec.tv_sec * 1_000_000 + time_spec.tv_nsec / 1_000)
    }
}

let timer = Timer()


class Base {
    var baseNum: Int = 1
    @inline(never)
    func compute() -> Int {
        return self.baseNum
    }
    init(num: Int){
        self.baseNum = num
    }
}

class DeriveDouble: Base {
    @inline(never)
    override func compute() -> Int {
        return self.baseNum * 2
    }
}

class DerivedTripple: Base {
    @inline(never)
    override func compute() -> Int {
        return self.baseNum * 3
    }
}

func Polymorphism() -> Double {
    let count = 100000
    var result: [Int] = [Int](repeating: 0, count: count)
    var result2: [Base] = [Base](repeating: DeriveDouble(num: 0), count: count)
    var result3: [Base] = [Base](repeating: DerivedTripple(num: 0), count: count)
    for i in 0..<count {
        result[i] = i
        result2[i] = DeriveDouble(num: i)
        result3[i] = DerivedTripple(num: i)
    }
    timer.start()
    for i in 0..<count {
        if (result[i] == i) {
            result2[i].baseNum = result2[i].compute();
            result3[i].baseNum = result3[i].compute();
        }
    }
    let time = timer.stop()
    var res = true
    for i in 0..<count {
       if (result2[i].baseNum != i * 2 || result3[i].baseNum != i * 3) {
            res = false
       }
    }
    if (!res) {
        print("result is wrong")
    }
    print("Property Access - Polymorphism:\t"+String(time)+"\tms");
    return time
}
_ = Polymorphism()

class Square {
    var length: Int = 0
    var width: Int = 0
    
    init(length: Int, width: Int) {
        self.length = length
        self.width = width
    }
}

func GenerateFakeRandomSquare() -> [Square] {
    let resource: [Square] = [Square](repeating: Square(length: Int.random(in: 1..<10), width: Int.random(in: 1..<10)), count: 10)
    return resource
}

func SingleICClass() -> Double {
    let container = GenerateFakeRandomSquare()
    let count = 1000000
    let arraySize: Int = 3
    var length_res = [Int](repeating: 0, count: arraySize)
    var width_res = [Int](repeating: 0, count: arraySize)
    timer.start()
    for i in 0..<count {
        for j in 0..<container.count {
            let thisBox = container[j]
            length_res[i % arraySize] += thisBox.length
            width_res[i % arraySize] += thisBox.width
        }
    }
    let time = timer.stop()
    var res = 0
    for j in 0..<arraySize {
        res += length_res[j] + width_res[j]
    }
    print(res)
    print("Property Access - SingleICClass:\t"+String(time)+"\tms");
    return time
}
_ = SingleICClass()

// 不可扩展属性
class Person {
    var name: String
    var age: Int
    init(name: String, age: Int) {
        self.name = name
        self.age = age
    }

    @inline(never)
    func equal(cmp: Person) -> Bool {
        return self.name == cmp.name && self.age == cmp.age;
    }
}

class Student: Person {
    var university: String
    
    init(name: String, age: Int, university: String) {
        self.university = university
        super.init(name: name, age: age)
    }
}

func GenerateFakeRandomPersons() -> [Person] {
    let resources: [Person] = [Person](repeating: Person(name: "John", age: Int.random(in: 1..<50)), count: 3)
    return resources
}

func NoneExtension() -> Double {
    let person1 = Person(name: "John", age: 12);
    let person2 = Person(name: "John", age: 13);
    let person3 = Person(name: "John", age: 14);
    let resourcesPerson: [Person] = [person1, person2, person3]
    let student1 = Student(name: "John", age: 21, university: "UNY")
    let count: Int = 100000
    let arraySize: Int = 3
    var test: Person = person1;
    var res: [Int] = [Int](repeating: 0, count: arraySize)
    timer.start()
    for i in 0..<count {
        // if (resourcesPerson[i % resourcesPerson.count].name == "John" && resourcesPerson[i % resourcesPerson.count].age <= 20) {
        //     if (student1.university == "UNY") {
        //         res[i % arraySize] += 1
        //     }
        // } else {
        //     if (student1.age == 21) {
        //         res[i % arraySize] += 2
        //     }
        // }
        let a = resourcesPerson[i % arraySize];
        if (a.equal(cmp: person1)) {
            let b = resourcesPerson[(i + 1) % arraySize];
            if (b.equal(cmp: person2)) {
                let c = resourcesPerson[(i + 2) % arraySize];
                if (c.equal(cmp: person3)) {
                    test = c;
                }
            }
        }
        if (i % arraySize == 0 && test.equal(cmp: person3)) {
            res[i % 3] += 1
            test = person1
        }
        if (i % arraySize == 0 && test.equal(cmp: person1)) {
            res[i % 3] += 1
            test = person2
        }
    }
    let time = timer.stop()
    var sum = 0
    for j in 0..<arraySize {
        sum += res[j]
    }
    print(sum)
    print("Property Access - NoneExtension:\t"+String(time)+"\tms");
    return time
}
_ = NoneExtension()



class MyObject {
    var num: Int;
    var age: Int;
    var Getter: Int {
        get {
            return self.num;
        }
    }
    var Setter: Int {
        get {
            return self.age;
        }
        set (newValue){
            self.age = newValue;
        }
    }
    init(num: Int, age: Int) {
        self.num = num;
        self.age = age;
    }
}

func GenerateNumArray(_ count: Int) -> [Int] {
    var numArray:[Int] = [Int](repeating: 0, count: count);
    for i in 0..<count {
        numArray[i] = i + 1;
    }
    return numArray;
}

func GenerateResultArray(_ count: Int) -> [MyObject] {
    var result: [MyObject] = [MyObject](repeating: MyObject(num: 0, age: 1), count: count);
    for i in 0..<count {
        result[i] = MyObject(num: i, age: i);
    }
    return result;
}

// Getter & Setter
func GetterSetterTest() -> [MyObject] {
    let count = 10000000
    let result: [MyObject] = GenerateResultArray(10);
    let numArray: [Int] = GenerateNumArray(10);
    timer.start()
    let resLen = result.count;
    for i in 0..<count {
        let index = i % resLen;
        let ret = result[index];
        if (ret.Getter == numArray[index]) {
            ret.Setter = index;
        }
    }
    let time = timer.stop()
    print("Property Access - GetterSetterTest:\t"+String(time)+"\tms");
    return result
}
let result = GetterSetterTest();
let numArray =  GenerateNumArray(result.count);
var sum = 0;
var res = true;
for i in 0..<result.count {
    sum += result[i].num
    if (result[i].Setter != numArray[i]) {
        res = false;
        break;
    }
}
print(res);
print(sum);

// All Number object
class Grades {
    var math: Double
    var english: Double
    var physics: Double
    var chemistry: Double
    
    init(math: Double, english: Double, physics: Double, chemistry: Double) {
        self.math = math
        self.english = english
        self.physics = physics
        self.chemistry = chemistry
    }
}

func GenerateFakeRandomGrades() -> [Grades] {
    let resource: [Grades] = [Grades](repeating: Grades(math: Double.random(in: 1..<100), english: Double.random(in: 1..<100), physics: Double.random(in: 1..<100), chemistry: Double.random(in: 1..<100)), count: 10)
    return resource
}

func NumberObject() -> Double {
    let count: Int = 1000000
    let arraySize: Int = 2
    let myGrades = GenerateFakeRandomGrades()
    var res = [Double](repeating: 0, count: (arraySize + 1))
    timer.start()
    let gradesLength = myGrades.count - 1;
    for i in 0..<count {
        var ret = res[i & arraySize];
        var grades = myGrades[i & gradesLength];
        ret += grades.math
        ret += grades.english
        ret += grades.physics
        ret += grades.chemistry
        res[i & arraySize] = ret;
    }
    let time = timer.stop()
    var sum = 0.0
    for j in 0..<arraySize {
        sum += res[j]
    }
    print(sum)
    print("Property Access - NumberObject:\t"+String(time)+"\tms");
    return time
}
_ = NumberObject()
